package com.yunchao.bank.main.presentation.screen

import android.app.Application
import com.yunchao.bank.business.domain.model.BusinessInfoModel
import com.yunchao.bank.main.data.model.MetadataUserModel
import com.yunchao.bank.main.domain.repository.NavHostRepository
import com.yunchao.bank.main.domain.repository.SocketErrorMessage
import com.yunchao.feature.base.data.CustomAudioManager
import com.yunchao.feature.base.domain.result.errorMsg
import com.yunchao.feature.base.presentation.viewmodel.BaseAction
import com.yunchao.feature.base.presentation.viewmodel.BaseEffect
import com.yunchao.feature.base.presentation.viewmodel.BaseState
import com.yunchao.feature.base.presentation.viewmodel.BaseViewModel
import com.yunchao.library.socket.CommandReceiver
import com.yunchao.library.socket.TcpSocketClient
import com.yunchao.library.socket.config.ConnectionState
import com.yunchao.library.socket.config.MessageHeader
import com.yunchao.library.socket.config.TcpConfig
import com.yunchao.library.socket.model.card.CommandData

internal class MainViewModel(
    private val repository: NavHostRepository,
    private val customAudioManager: CustomAudioManager,
    private val context: Application
) : BaseViewModel<MainViewModel.UiState, MainViewModel.Action, MainViewModel.UiEffect>(
    UiState.Idle
) {

    private val tcpClientManager = TcpClientManager()

    init {
        handleError()
        handleBusinessInfo()
    }

    private fun handleBusinessInfo() = safeLaunch {
        repository.observeBusinessInfo().collect {
            sendAction(Action.LoadBusinessInfoSuccess(it))
        }
    }

    fun openDevice() = launchWithLoading {
        repository.openDevice()
            .onSuccess {
                postSideEffect(UiEffect.ShowDeviceSuccess("设备初始化成功")).apply {
                    customAudioManager.playSound(1)
                }
            }
            .onFailure {
                postSideEffect(UiEffect.ShowToast(it.errorMsg)).apply {
                    customAudioManager.playSound(
                        2
                    )
                }
            }
    }


    fun closeDevice() = safeLaunch { repository.closeDevice() }


    fun setupTcpClient() {
        tcpClientManager.setup()
        CommandReceiver.start(52020, this)
    }

    fun sendCardCommand(isStopSend: Boolean = false) = safeLaunch {
        if (isStopSend) {
            return@safeLaunch
        }
        repository.sendCardCommand(true)
    }

    fun onCommandReceived(command: CommandData) = safeLaunch {
        repository.handleCardCommand(command)
            .onSuccess {

                // 根据当前期待的类型创建人员信息
                val cardInfo = PersonCardInfo(
                    type = expectedPersonType,
                    commandData = commandData
                )


                sendAction(Action.LoadMetadataUserSuccess(it))
            }
            .onFailure { onError(it) }
    }


    // 确定下一个需要采集的人员类型
    private fun determineNextPersonType(state: MultiCardState): PersonType? {
        return when {
            state.operator == null -> PersonType.OPERATOR
            state.authorizer == null -> PersonType.AUTHORIZER
            state.companion == null -> PersonType.COMPANION
            else -> null // 全部采集完成
        }
    }

    override fun onError(error: Throwable) = safeLaunch {
        Timber.e(error, "CommandReceiver error: $error")
        sendCardCommand()
    }

    private fun launchWithLoading(block: suspend () -> Unit) = safeLaunch(
        onStart = { postSideEffect(UiEffect.ShowLoading) },
        onFinally = { postSideEffect(UiEffect.HideLoading) },
        block = block
    )

    private fun showSuccessDialog(message: String) {
        postSideEffect(UiEffect.DialogEffect.Success(message))
    }

    private fun showWarningDialog(message: String) {
        postSideEffect(UiEffect.DialogEffect.Warning(message))
    }

    private fun showErrorDialog(message: String) {
        postSideEffect(UiEffect.DialogEffect.Error(message))
    }

    private fun handleError() = safeLaunch {
        repository.observeSocketError().collect { socketError ->
            when (socketError) {
                is SocketErrorMessage.Success -> showSuccessDialog(socketError.message)
                is SocketErrorMessage.Error -> showErrorDialog(socketError.message)
                is SocketErrorMessage.Warning -> showWarningDialog(socketError.message)
            }
        }
    }


    override fun onCleared() {
        closeDevice()
        CommandReceiver.stop()
        tcpClientManager.disconnect()
        super.onCleared()
    }

    // region [State/Effect/Action]
    internal sealed interface UiState : BaseState {
        data object Idle : UiState
        data class Connected(val connectionState: ConnectionState) : UiState
        data class BusinessSuccess(val businessModel: BusinessInfoModel) : UiState
        data class MetadataUserSuccess(
            val operator: MetadataUserModel? = null,
            val authorizer: MetadataUserModel? = null,
            val companion: MetadataUserModel? = null,
            val isComplete: Boolean = false
        ) : UiState
    }

    internal sealed interface UiEffect : BaseEffect {
        data object ShowLoading : UiEffect
        data object HideLoading : UiEffect
        data class ShowToast(val msg: String) : UiEffect
        data class ShowDeviceSuccess(val msg: String) : UiEffect

        sealed class DialogEffect(open val message: String) : UiEffect {
            data class Success(override val message: String) : DialogEffect(message)
            data class Warning(override val message: String) : DialogEffect(message)
            data class Error(override val message: String) : DialogEffect(message)
        }
    }

    internal sealed interface Action : BaseAction<UiState> {
        data class LoadMetadataUserSuccess(private val metadataUserModel: MetadataUserModel?) :
            Action {
            override fun reduce(state: UiState) = UiState.MetadataUserSuccess(metadataUserModel)
        }

        data class LoadBusinessInfoSuccess(private val businessModel: BusinessInfoModel) : Action {
            override fun reduce(state: UiState) = UiState.BusinessSuccess(businessModel)
        }

        data class UpdateConnectionState(private val connectionState: ConnectionState) : Action {
            override fun reduce(state: UiState) = UiState.Connected(connectionState)
        }
    }
    // endregion


    // region [Inner Classes]
    private inner class TcpClientManager {
        private var tcpSocketClient: TcpSocketClient? = null

        fun setup() {
            if (tcpSocketClient != null) return

            tcpSocketClient = TcpSocketClient(
                config = TcpConfig(host = "192.168.10.80", port = 50002),
                externalScope = viewModelScope,
                context = context,
            ).apply {
                startMessageCollection()
                startConnectionMonitoring()
                start()
            }

        }

        fun startHeartbeat() {
            tcpSocketClient?.startHeartbeat()
        }

        fun disconnect() {
            tcpSocketClient?.close()
            tcpSocketClient = null
        }

        private fun TcpSocketClient.startMessageCollection() = safeLaunch {
            incomingMessages.collect { message ->
                repository.processIncomingMessage(message)?.let {
                    sendTcpMessage(message.header, it)
                }
            }
        }


        private fun TcpSocketClient.startConnectionMonitoring() = safeLaunch {
            connectionState.collect { state ->
                sendAction(Action.UpdateConnectionState(state))
            }
        }

        suspend fun sendTcpMessage(header: MessageHeader, bodyBytes: ByteArray) {
            try {
                tcpSocketClient?.sendMessage(
                    direction = header.direction,
                    transport = header.transport,
                    bodyBytes = repository.processEncryption(header, bodyBytes),
                    isPacket = header.isPacket,
                    indicator = header.indicator
                )
            } catch (e: Exception) {
                Timber.e(e, "发送消息失败")
            }
        }

    }
}